package com.dysm.spring.security.config;

import com.dysm.spring.security.service.MyClientService;
import com.dysm.spring.security.service.MyUserService;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableAuthorizationServer
public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {

    @Resource
    private MyClientService myClientService;

    @Resource
    private MyUserService myUserService;

    @Resource
    private ApprovalStore approvalStore;

    @Resource
    private TokenStore tokenStore;

    @Resource(name = "oAuth2AuthenticationManager")
    private AuthenticationManager oAuth2AuthenticationManager;

    @Resource(name = "passwordGrantTokenAuthenticationManager")
    private AuthenticationManager passwordGrantTokenAuthenticationManager;

    @Resource
    private AuthorizationCodeServices authorizationCodeServices;

    @Resource
    private DefaultTokenServices tokenServices;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients();// 可接受form表单访问
        security.checkTokenAccess("permitAll()").tokenKeyAccess("permitAll()");// 允许校验 token，匿名访问
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(myClientService);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .userDetailsService(myUserService)
                .approvalStore(approvalStore)
                .authenticationManager(oAuth2AuthenticationManager)
                .authorizationCodeServices(authorizationCodeServices)
                .tokenStore(tokenStore)
        .tokenGranter(tokenGranter())
        .tokenServices(tokenServices);

    }

    private TokenGranter tokenGranter() {
        return new TokenGranter() {
            private CompositeTokenGranter delegate;
            @Override
            public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
                if (delegate == null) {
                    delegate = new CompositeTokenGranter(getDefaultTokeGranters());
                }
                return delegate.grant(grantType, tokenRequest);
            }
        };
    }

    private List<TokenGranter> getDefaultTokeGranters() {
        List<TokenGranter> tokenGranters = new ArrayList<>();
        DefaultOAuth2RequestFactory requestFactory = new DefaultOAuth2RequestFactory(myClientService);
        // 授权码授权方式，授权通过返回code，再通过code置换token
        AuthorizationCodeTokenGranter authorizationCodeTokenGranter = new AuthorizationCodeTokenGranter(tokenServices,
                authorizationCodeServices, myClientService, requestFactory);
        tokenGranters.add(authorizationCodeTokenGranter);

        // 简易授权模式，授权通过直接返回token
        ImplicitTokenGranter implicitTokenGranter = new ImplicitTokenGranter(tokenServices, myClientService, requestFactory);
        tokenGranters.add(implicitTokenGranter);

        // 用户名和密码模式，传入用户名和密码，校验通过会返回token
        ResourceOwnerPasswordTokenGranter passwordTokenGranter = new ResourceOwnerPasswordTokenGranter(
                passwordGrantTokenAuthenticationManager, tokenServices, myClientService, requestFactory);
        tokenGranters.add(passwordTokenGranter);

        // 客户端模式，验证客户端id和秘钥，校验通过会返回token
        // 这种方式给出的令牌，是针对第三方应用的，而不是针对用户的，即有可能多个用户共享同一个令牌
        ClientCredentialsTokenGranter credentialsTokenGranter = new ClientCredentialsTokenGranter(tokenServices,
                myClientService, requestFactory);
        tokenGranters.add(credentialsTokenGranter);
        return tokenGranters;
    }

}
